package cn.itcast.nems.bpm.web.controller.b;

import cn.hutool.core.lang.Assert;
import cn.itcast.ic.common.bean.Page;
import cn.itcast.nems.approval.bpm.eventstrategy.BpmEventStrategy;
import cn.itcast.nems.approval.dao.entity.ApprovalForm;
import cn.itcast.nems.approval.enumeration.ApprovalFormStateEnum;
import cn.itcast.nems.approval.service.ApprovalAdditionalFormService;
import cn.itcast.nems.approval.service.ApprovalConfigService;
import cn.itcast.nems.approval.service.ApprovalFormService;
import cn.itcast.nems.approval.service.ApprovalFormService.ApproveResult;
import cn.itcast.nems.approval.service.ApprovalFormService.JournalType;
import cn.itcast.nems.bpm.web.vo.ApproveOpinionVO;
import cn.itcast.nems.common.PageQueryModel;
import cn.itcast.nems.common.account.AccountHolder;
import cn.itcast.nems.common.account.AccountInfo;
import cn.itcast.nems.employee.service.EmployeeService;
import cn.itcast.nems.manager.activity.BpmWorkflowManager;
import cn.itcast.nems.manager.redis.RedisUtil;
import cn.itcast.nems.studentrecord.dao.dataobject.ApprovalFormQueryModel;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

@RestController("bNewBpmWorkflowController")
@RequestMapping("/b/bpm/approval/form")
@RequiredArgsConstructor
public class BpmWorkflowController {

    private final ApprovalFormService approvalFormService;
    private final ApprovalConfigService approvalConfigService;
    private final BpmWorkflowManager bpmWorkflowManager;
    private final ApprovalAdditionalFormService approvalAdditionalFormService;
    // 事件处理策略类集合
    @Autowired
    private Set<BpmEventStrategy> eventStrategySets;
    // 通过凭单类型快速获取策略类
    private Map<String, BpmEventStrategy> eventStrategyMaps;
    private final RedisUtil redisUtil;
    private final EmployeeService employeeService;

    @PostConstruct
    public void init() {
        eventStrategyMaps = new HashMap<>();
        for (BpmEventStrategy bpmEventStrategy : eventStrategySets) {
            eventStrategyMaps.put(bpmEventStrategy.getStrategyName(), bpmEventStrategy);
        }
    }

    /**
     * 功能描述：获取审批流
     *
     * @param formID 审批单ID
     * @return 审批流数据
     * @author 刘曙
     * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
     * @since 2023年11月23日
     */
    @GetMapping(value = "/{formID}/flow")
    public ResponseEntity<?> flow(@PathVariable("formID") final String formID) {
        Map<String, Object> historicTaskMap = new HashMap<>();
        List<String> formIDs = approvalFormService.getFormIDs(formID);
        if (formIDs.size() == 1) {
            historicTaskMap.put("flow", bpmWorkflowManager.listHistoricTaskInstance(formID));
        } else if (formIDs.size() > 1) {
            // 多个formID处理
            LinkedList<Map<String, Object>> resultList = new LinkedList<>();
            for (int i = 0; i < formIDs.size(); i++) {
                String formId = formIDs.get(i);
                List<Map<String, Object>> maps = (List<Map<String, Object>>) bpmWorkflowManager.listHistoricTaskInstance(formId);
                if (maps == null) {
                    continue;
                }
                resultList.addAll(maps);
            }
            historicTaskMap.put("flow", resultList);
        }
        ApprovalForm approvalForm = approvalFormService.getById(formID);
        if (approvalForm != null) {
            if (!approvalForm.getState().equals(ApprovalFormStateEnum.FINISHED.name())) {
                historicTaskMap.put("form_description", approvalForm.getFormDescription());
            }
        }
        return ResponseEntity.ok().body(historicTaskMap.size() == 0 ? null : historicTaskMap);
    }

    /**
     * 功能描述：验证当前用户审批权限
     *
     * @param formID
     * @return true｜有权限、false｜无权限
     * @author 刘曙
     * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
     * @since 2023年11月23日
     */
    @GetMapping(value = "/{formID}/permission/valid")
    public ResponseEntity<?> permission(@PathVariable("formID") final String formID) {
        final String currentMail = AccountHolder.getRequiredAccountInfo().getEmail();
        return ResponseEntity.ok().body(bpmWorkflowManager.hasApproval(formID, currentMail));
    }

    /**
     * 获取当前审批节点详情信息
     *
     * @param formID
     * @return 当前审批节点详情信息
     **/
    @GetMapping(value = "/{formID}/find/task/node")
    public ResponseEntity<?> findFindTaskNodeInfo(@PathVariable("formID") final String formID) {
        final String currentMail = AccountHolder.getRequiredAccountInfo().getEmail();
        return ResponseEntity.ok().body(bpmWorkflowManager.findFindTaskNodeInfo(formID, currentMail));
    }

    /**
     * 获取当前审批节点nodeKey
     *
     * @param formID
     * @return 当前审批节点nodeKey
     **/
    @GetMapping(value = "/{formID}/find/task/node/key")
    public ResponseEntity<?> findFindTaskNodeKey(@PathVariable("formID") final String formID) {
        final String currentMail = AccountHolder.getRequiredAccountInfo().getEmail();
        return ResponseEntity.ok().body(bpmWorkflowManager.findFindTaskNodeKey(formID, currentMail));
    }

    /**
     * 功能描述：催办
     *
     * @param formID         催办的审批流
     * @param targetAssignee 指定要催办的人（邮箱）
     * @return
     * @author 刘曙
     * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
     * @since 2023年11月23日
     */
    @PostMapping(value = "/{formID}/remind")
    public ResponseEntity<Void> remind(@PathVariable("formID") String formID, @RequestParam final String targetAssignee) {
        ApprovalForm approvalForm = approvalFormService.getById(formID);
        Assert.notNull(approvalForm, "审批单ID[%s]无效，找不到对应的审批单！", formID);
        BpmEventStrategy bpmEventStrategy = bpmEventStrategy(approvalForm.getJournalType());
        bpmEventStrategy.remind(formID, targetAssignee);
        return ResponseEntity.ok().build();
    }

    /**
     * 功能描述：审批
     *
     * @param formID  审批流程单ID
     * @param opinion 审批结果 和 意见（JSON对象）
     * @return
     * @author 刘曙
     * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
     * @since 2023年11月23日
     */
    @PutMapping(value = "/{formID}/approve")
    public ResponseEntity<Void> approve(@PathVariable("formID") String formID, @RequestBody @Validated ApproveOpinionVO opinion) {
        ApprovalForm approvalForm = approvalFormService.getById(formID);
        Assert.notNull(approvalForm, "审批单ID[%s]无效，找不到对应的审批单！", formID);
        BpmEventStrategy bpmEventStrategy = bpmEventStrategy(approvalForm.getJournalType());
        if (opinion.getApplyType() != null  && opinion.getApplyType() == 3) {
            List<Map<String, Object>> maps = (List<Map<String, Object>>) bpmWorkflowManager.listHistoricTaskInstance(formID);
            if (maps.size() <= 2) {
                opinion.setApplyType(2);
            }
        }
        bpmEventStrategy.approve(formID, opinion.getResult() ? ApproveResult.APPROVE : ApproveResult.REJECT, opinion.getComment(), opinion.getForm(), opinion.getApplyType());
        return ResponseEntity.ok().build();
    }

    /**
     * 撤回
     *
     * @return
     */
    @PutMapping(value = "/{formID}/cancel")
    public ResponseEntity<Void> cancel(@PathVariable("formID") final String formID) {
        ApprovalForm approvalForm = approvalFormService.getById(formID);
        Assert.notNull(approvalForm, "审批单ID[%s]无效，找不到对应的审批单！", formID);
        BpmEventStrategy bpmEventStrategy = bpmEventStrategy(approvalForm.getJournalType());
        bpmEventStrategy.cancel(formID);
        return ResponseEntity.ok().build();
    }

    /**
     * 根据凭单类型获取待审批数据量
     *
     * @param journalCode 凭单类型
     * @return org.springframework.http.ResponseEntity<java.lang.Integer>
     * @author 高昌露
     * @since 2023/11/24 10:43
     * @deprecated 请使用 {@link cn.itcast.nems.bpm.web.controller.b.BpmWorkflowController#myTodoCount}
     */
    @Deprecated
    @GetMapping(value = "/{journalCode}/wait/approving")
    public ResponseEntity<Integer> waitApprovingCount(@PathVariable("journalCode") final String journalCode) {
        var record = approvalConfigService.getByJournalCode(journalCode);
        Assert.notNull(record, "凭单类型尚未配置！");
        List<String> formIds = bpmWorkflowManager.findWaitApproval(record.getBpmProcessKey(), AccountHolder.getRequiredAccountInfo().getEmail());
        ApprovalFormQueryModel queryModel = new ApprovalFormQueryModel();
        queryModel.setFlowCreatedBy(AccountHolder.getRequiredAccount());
        queryModel.setJournalType(journalCode);
        queryModel.setFormIds(formIds);
        List<ApprovalForm> waitApprovingList = approvalFormService.getWaitApprovalFormList(queryModel);
        return ResponseEntity.ok(CollectionUtils.isEmpty(waitApprovingList) ? 0 : waitApprovingList.size());
    }

    /**
     * 功能描述：获取待我审批单据数量
     *
     * @param type       单据类型
     * @param pageNumber 页码
     * @param pageSize   每页条数
     * @return
     * @author 刘曙
     * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
     * @since 2023年12月3日
     */
    @GetMapping(value = "/my-todo")
    public ResponseEntity<Page<ApprovalForm>> myTodo(@RequestParam(name = "type") final String type,
                                                     @RequestParam(name = "page_number") final Integer pageNumber,
                                                     @RequestParam(name = "page_size") Integer pageSize) {
        // TODO 获取待我审批的凭单列表（未完成实现）
        return ResponseEntity.ok(approvalFormService.listMyTodo(new PageQueryModel<>(pageNumber, pageSize, null), valueOfJournalType(type)));
    }

    /**
     * 功能描述：获取待我审批单据数量
     *
     * @param type 单据类型
     * @return 待我审批单据数量
     * @author 刘曙
     * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
     * @since 2023年12月3日
     */
    @GetMapping(value = "/my-todo/count")
    public ResponseEntity<Integer> myTodoCount(@RequestParam(name = "type") final String type) {
        return ResponseEntity.ok(approvalFormService.getMyTodoCount(valueOfJournalType(type)));
    }

    private JournalType valueOfJournalType(final String type) {
        JournalType journalType = null;
        try {
            journalType = JournalType.valueOf(type);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException(String.format("凭单类型[%s]不存在或错误！", type));
        }

        return journalType;
    }

    BpmEventStrategy bpmEventStrategy(String eventStrategyKey) {
        if (JournalType.EXCHANGE.name().equalsIgnoreCase(eventStrategyKey) ||
                JournalType.DELAY.name().equalsIgnoreCase(eventStrategyKey) ||
                JournalType.SUSPENSION.name().equalsIgnoreCase(eventStrategyKey)) {
            eventStrategyKey = JournalType.TRANSFER.name();
        }
        return eventStrategyMaps.get(eventStrategyKey);
    }

    @GetMapping(value = "/{formID}/additional/forms")
    public ResponseEntity<Map<String, Object>> formAdditionalFroms(@PathVariable("formID") String formId) {
        Map<String, Object> additionalInfo = approvalAdditionalFormService.formAdditionalFroms(formId);
        return ResponseEntity.ok(additionalInfo);
    }

    /**
     * 获取指定凭单正在审批人列表
     *
     * @param formId
     * @return
     */
    @GetMapping(value = "/{formID}/beat")
    public ResponseEntity<Set<AccountInfo>> beat(@PathVariable("formID") String formId) {
        AccountInfo requiredAccountInfo = AccountHolder.getRequiredAccountInfo();
        String key = String.format("EMSV2_JOURNAL_APPROVE_USER_BEAT:%s:*", formId);
        Set<String> keys = redisUtil.getKeys(key);
        Set<AccountInfo> AccountInfos = keys.stream()
                .filter(k -> !k.equals(String.format("EMSV2_JOURNAL_APPROVE_USER_BEAT:%s:%s", formId, requiredAccountInfo.getEmail())))
                .map(cacheKeys -> {
                    return (AccountInfo) redisUtil.get(cacheKeys);
                }).collect(Collectors.toSet());
        return ResponseEntity.ok(AccountInfos.size() == 0 ? null : AccountInfos);
    }

    /**
     * 发起指定凭单当前审批节点正在审批人心跳
     *
     * @param formId
     * @return
     * @throws Exception
     */
    @PostMapping(value = "/{formID}/beat")
    public ResponseEntity<Boolean> createBeat(@PathVariable("formID") String formId) throws Exception {
        AccountInfo requiredAccountInfo = AccountHolder.getRequiredAccountInfo();
        String key = String.format("EMSV2_JOURNAL_APPROVE_USER_BEAT:%s:%s", formId, requiredAccountInfo.getEmail());
        if (redisUtil.hasKey(key)) {
            return ResponseEntity.ok(Boolean.TRUE);
        }
        redisUtil.set(key, requiredAccountInfo, 10L);
        return ResponseEntity.ok(Boolean.TRUE);
    }
}
